﻿using Red.Controls.ViewModels;
using System.Windows;
using System.Windows.Controls;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Windows.Input;

namespace Red.Controls
{
    public struct ObjectsPaletteItemInfo
    {
        public string Name { get; set; }
        public string Category { get; set; }
        public string ThumbnailPath { get; set; }
        public string Path { get; set; }
    }

    /// <summary>
    /// Interaction logic for ObjectsPalette.xaml
    /// </summary>
    public partial class ObjectsPalette : UserControl
    {
        #region Data

        private static Dictionary< string/*Editor*/, ObjectsPaletteRootCategory > _objectsPalettesDefinitions = new Dictionary<string, ObjectsPaletteRootCategory>();
        private ObjectsPaletteRootCategory _rootCategory;
        private HashSet< ObjectsPaletteItem > _favorites;

        #endregion Data

        #region Properties

        public static readonly DependencyProperty EditorNameProperty = DependencyProperty.Register(
            "EditorName", typeof( string ), typeof( ObjectsPalette ), new FrameworkPropertyMetadata( null, EditorNamePropertyChanged ) );

        /// <summary>
        ///     Palette categories and items will be loaded from file 'Data/{EditorName}.objectspalette'
        /// </summary>
        public string EditorName
        {
            get { return (string)GetValue( EditorNameProperty ); }
            set { SetValue( EditorNameProperty, value ); }
        }

        /// <summary>
        ///     This property should be bound with editor's UserSettings loading/saving
        /// </summary>
        public IEnumerable<ObjectsPaletteItem> Favorites
        {
            get
            {
                return _favorites;
            }
            set
            {
                var newFav = new HashSet<ObjectsPaletteItem>( value );
                if ( newFav != _favorites )
                {
                    _favorites = newFav;
                    if ( _rootCategory != null )
                        _rootCategory.Favorites = newFav.ToList();
                }
            }
        }

        #endregion Properties

        #region C-tor

        public ObjectsPalette()
        {
            InitializeComponent();
        }

        #endregion C-tor

        public bool AddToFavorites( ObjectsPaletteItem item )
        {
            if ( _favorites.Contains( item ) )
                return false;

            _favorites.Add( item );
            _rootCategory.Favorites.Add( item );

            return true;
        }

        public bool RemoveFromFavorites( ObjectsPaletteItem item )
        {
            if ( !_favorites.Contains( item ) )
                return false;

            _favorites.Remove( item );
            _rootCategory.Favorites.Remove( item );

            return true;
        }

        public static void EditorNamePropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs baseValue )
        {
            var palette = d as ObjectsPalette;
            var editorName = baseValue.NewValue as string;
            if ( editorName != null )
            {
                ObjectsPaletteRootCategory rootDataItem;
                _objectsPalettesDefinitions.TryGetValue( editorName, out rootDataItem );
                if ( rootDataItem == null )
                {
                    // prepare root data item
                    var itemsInfo = PreCache( editorName );
                    rootDataItem = new ObjectsPaletteRootCategory();
                    PrepareDataItems( itemsInfo, rootDataItem );
                    _objectsPalettesDefinitions.Add( editorName, rootDataItem );
                }

                if ( palette.Favorites != null )
                    rootDataItem.Favorites = palette.Favorites.ToList();

                palette._stacker.DataContext = rootDataItem;
                palette._rootCategory = rootDataItem;
            }
        }

        #region Private methods

        private static void PrepareDataItems( IEnumerable< ObjectsPaletteItemInfo > itemsInfo, ObjectsPaletteCategory rootCategory )
        {
            Dictionary< string, ObjectsPaletteCategory > categories = new Dictionary<string, ObjectsPaletteCategory>();

            foreach ( var itemInfo in itemsInfo )
            {
                var categoryName = itemInfo.Category;
                ObjectsPaletteCategory category = null;
                categories.TryGetValue( categoryName, out category );
                if ( category == null )
                {
                    // create missing categories for parts along the way to the category that contains a new item
                    string[] subcategoryNames = categoryName.Split( '.' );
                    string currentSubcategoryName = ""; // track current category name (for mapping purposes)
                    ObjectsPaletteCategory parent = rootCategory; // track previous category to link hierarchy
                    foreach( var subcategoryName in subcategoryNames )
                    {
                        currentSubcategoryName += subcategoryName;
                        ObjectsPaletteCategory subcategory = null;
                        categories.TryGetValue( currentSubcategoryName, out subcategory );
                        if ( subcategory == null )
                        {
                            subcategory = new ObjectsPaletteCategory( subcategoryName, parent );
                            categories.Add( currentSubcategoryName, subcategory );
                            parent.Subcategories.Add( subcategory );
                        }
                        
                        parent = subcategory;
                        currentSubcategoryName += ".";
                    }
                    category = parent;
                }
                category.Items.Add( new ObjectsPaletteItem( itemInfo, category ) ); // add new item in the last category
            }
        }

        private static IEnumerable< ObjectsPaletteItemInfo > PreCache( string fileName )
        {
            try
            {
                XDocument doc = XDocument.Load( "Data/" + fileName + ".objectspalette" );

                var data = from tag in doc.Descendants( "ObjectsPaletteItem" )
                           select new ObjectsPaletteItemInfo
                           {
                               Name = tag.Attribute( "Name" )?.Value,
                               Category = tag.Attribute( "Category" )?.Value,
                               ThumbnailPath = tag.Attribute( "ThumbnailPath" )?.Value,
                               Path = tag.Attribute( "Path" )?.Value,
                           };

                return data.ToList();
            }
            catch ( System.IO.IOException )
            {
                return null;
            }
        }

        #endregion Private methods
    }
}
